# -*- coding=utf-8 -*-
import sys
import datetime
import traceback
from enum import Enum
from enum import unique
from utils.region import region_code

sys.path.append(__file__)


@unique
class IdStatus(Enum):
    """
    Enum Class marks Row object status
    """
    INITIAL = 1
    DOWNLOAD = 2
    FORCE = 3
    SKIP = 4
    SUCCESS = 5
    FAILURE = 6
    ERROR = 7


class IdCardObject(object):
    """
    Class of identity card Object
    """
    id_number = None  # identity card string
    _name = None  # Sheet name in Excel file
    column_name = None  # column name in sheet, list
    row_value = None  # row value in sheet, list
    column_value = None  # {column: value}, dict
    position = None  # row index in sheet
    temporary_url = None  # download url
    storage_absolute_path = None  # download file storage file
    status = None  # object status
    # md5_result = None               # object hash md5 result


class IdentityCard(object):
    factor = (7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2)
    last_bit = ("1", "0", "X", "9", "8", "7", "6", "5", "4", "3", "2")
    area = {"11": "北京", "12": "天津", "13": "河北", "14": "山西", "15": "内蒙古", "21": "辽宁", "22": "吉林",
            "23": "黑龙江", "31": "上海", "32": "江苏", "33": "浙江", "34": "安徽", "35": "福建", "36": "江西",
            "37": "山东", "41": "河南", "42": "湖北", "43": "湖南", "44": "广东", "45": "广西", "46": "海南",
            "50": "重庆", "51": "四川", "52": "贵州", "53": "云南", "54": "西藏", "61": "陕西", "62": "甘肃",
            "63": "青海", "64": "宁夏", "65": "新疆", "71": "台湾", "81": "香港", "82": "澳门", "91": "国外"}

    def __init__(self, **kwargs):
        self._id_number = None
        self._province = None
        self._city = None
        self._county = None
        self._birthday = None
        self._police = None
        self._gender = None
        self._verification_code = None

        self._params = self.get_params(**kwargs)  # if exists id_number, then set self._id_number

    @property
    def id_number(self):
        return self._id_number

    @id_number.setter
    def id_number(self, id_number):
        """
        input identity card number, analyze information
        :param id_number: identity card number
        :return: id_number
        """
        self._init_property()

        self.verify_id_number(id_number)
        self._id_number = str(id_number).replace(" ", "")
        return self._id_number

    def verify_id_number(self, id_number):
        """
        Verify identity information is valid
        :param id_number: identity card number
        :return:
        """
        id_number = "{s:.0f}".format(s=id_number) if isinstance(id_number, (float,)) else id_number

        assert isinstance(id_number, (str, int)) and str(id_number).strip(), "param id_number must be string or int"

        _id_number = str(id_number).replace(" ", "")
        assert len(_id_number) == 18, f"{id_number} must be 18 bit"
        assert _id_number[0:17].isdigit(), f"{id_number} include invalid character"

        self.get_province(_id_number)
        self.get_city(_id_number)
        self.get_county(_id_number)
        self.get_birthday(_id_number)
        self.get_police(_id_number)
        self.get_gender(_id_number)
        self.get_verification_code(_id_number)

    def get_province(self, id_number):
        """
        Get province information
        :param id_number: identity card number
        :return: Province name
        """
        try:
            assert id_number[0:2] in self.area.keys(), "param id_number area invalid"
            self._province = self.area.get(id_number[0:2])
            return self._province
        except AssertionError:
            print(f"{id_number} province invalid")
            raise AssertionError(f"{id_number} province invalid")
        except Exception as e:
            print("get_province : {e}".format(e=traceback.format_exc()))
            raise

    def get_city(self, id_number):
        """
        Get city information
        :param id_number: identity card number
        :return: city name
        """
        try:
            _city_code = id_number[0:4] + "00"
            self._city = region_code.get(_city_code, "未知")
            return self._city
        except Exception as e:
            print("get_city : {e}".format(e=traceback.format_exc()))
            raise

    def get_county(self, id_number):
        """
        Get county information
        :param id_number: identity card number
        :return: county name
        """
        try:
            _county_code = id_number[0:6]
            self._county = region_code.get(_county_code, "未知")
            return self._county
        except Exception as e:
            print("get_county : {e}".format(e=traceback.format_exc()))
            raise

    def get_birthday(self, id_number):
        """
        Get date of birth
        :param id_number:
        :return:
        """
        try:
            _year = int(id_number[6:10])
            _month = int(id_number[10:12])
            _day = int(id_number[12:14])
            self._birthday = datetime.date(_year, _month, _day).strftime("%Y-%m-%d")
            return self._birthday
        except ValueError:
            print(f"{id_number} birthday invalid")
            raise ValueError(f"{id_number} birthday invalid")
        except Exception as e:
            print("get_birthday : {e}".format(e=traceback.format_exc()))
            raise

    def get_gender(self, id_number):
        """
        Get sex information
        :param id_number:
        :return:
        """
        try:
            _gender = int(id_number[-2])
            self._gender = "女" if _gender % 2 == 0 else "男"
            return self._gender
        except Exception as e:
            print("get_gender : {e}".format(e=traceback.format_exc()))
            raise

    def get_police(self, id_number):
        """
        Get birth no. that issued by the police
        :param id_number:
        :return:
        """
        try:
            self._police = id_number[14:16]
            return self._police
        except Exception as e:
            print("get_police : {e}".format(e=traceback.format_exc()))
            raise

    def get_verification_code(self, id_number):
        """
        Get 18th verification code
        :param id_number:
        :return:
        """
        _func = lambda x, y: int(x) * int(y)
        try:
            _id_number_list = list(id_number[0:17])
            _result = sum(list(map(_func, _id_number_list, self.factor))) % 11
            if id_number[-1] == self.last_bit[_result]:
                self._verification_code = True
                return self._verification_code
            else:
                raise ValueError(f"{id_number} verification code invalid")
        except ValueError as e:
            print(e)
            raise
        except Exception as e:
            print("get_verification_code : {e}".format(e=traceback.format_exc()))
            raise

    @property
    def province(self):
        return self._province

    @property
    def city(self):
        return self._city

    @property
    def county(self):
        return self._county

    @property
    def birthday(self):
        return self._birthday

    @property
    def police(self):
        return self._police

    @property
    def gender(self):
        return self._gender

    @property
    def verification_code(self):
        return self._verification_code

    def _init_property(self):
        """
        Reinitialize property values
        :return:
        """
        self._id_number = None
        self._province = None
        self._city = None
        self._county = None
        self._birthday = None
        self._police = None
        self._gender = None
        self._verification_code = None

    def get_params(self, **kwargs):
        """
        get parameters from kwargs,
        check parameter, if Instance has function which get parameter
        :param kwargs:
        :return:
        """
        _params = dict()
        try:
            for k, v in kwargs.items():
                _param_name = "_get_param_{name}".format(name=str(k).strip())
                if hasattr(self, _param_name):
                    _function = getattr(self, _param_name)
                    _params[k] = _function(**kwargs)
                else:
                    _params[k] = v
            return _params
        except Exception as e:
            print("get_params error {e}".format(e=str(e)))
            raise

    def _get_param_id_number(self, **kwargs):
        """
        handle id_number, if it exists
        :param kwargs:
        :return:
        """
        if 'id_number' in kwargs.keys():
            self.id_number = kwargs.get('id_number')
            return self.id_number
        else:
            return None


if __name__ == "__main__":
    no1 = " 411702199003077310"
    no2 = " 420902199003077312 "
    no3 = " 652801200 103073747 "

    inst1 = IdentityCard(id_number=no1)

    inst1.id_number = no1
    print("###" * 30)
    print(inst1.__dict__)
    # print("###" * 30)
    # print(inst1.id_number)
    # print(inst1.province)
    # print(inst1.city)
    # print(inst1.county)
    # print(inst1.birthday)
    # print(inst1.police)
    # print(inst1.gender)
    # print(inst1.verification_code)
    print("###" * 30)
    inst1.id_number = no2
    print(inst1.__dict__)
    print("###" * 30)
    inst1.id_number = no3
    print(inst1.__dict__)
